// BugFix.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "BugFix.h"
#include "Vfw.h"
#include <iostream>
#include <boost/statechart/event.hpp> 
#include <boost/statechart/state_machine.hpp> 
#include <boost/statechart/simple_state.hpp> 
#include <boost/statechart/state.hpp>
#include <boost/statechart/custom_reaction.hpp>
#include <boost/statechart/transition.hpp>
#include <boost/statechart/result.hpp>
#include <mmsystem.h>
#pragma comment(lib, "vfw32.lib")

namespace fsm = boost::statechart;


#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
std::wstring file_name = L"D:\\VOA\\5. Deep Six It Is Well Hidden.mp3";

// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

static std::wstring FILE_NAME = L"";
HWND _playerWnd;
struct OpenFileEvent: public fsm::event< OpenFileEvent >{
    std::wstring file_name;
    OpenFileEvent(const std::wstring p_file_name):file_name(p_file_name){}
};

struct UpdatePosEvent: public fsm::event< UpdatePosEvent >{
    long pos;
    UpdatePosEvent(long position): pos(position){}
};

class WaitingForFileState;
class PlayingState;

class PlayerFsm : public fsm::state_machine< PlayerFsm,  WaitingForFileState >
{
public:
    friend class WaitingForFileState;
    friend class HandlingFileState;
    friend class PlayingState;
};

class WaitingForFileState:
    public fsm::simple_state< WaitingForFileState, PlayerFsm >
{
public:
    typedef fsm::custom_reaction< OpenFileEvent > reactions;
    fsm::result react( const OpenFileEvent& evt)
        {
            std::cout << "OpenFileEvent is received " << std::endl;
            FILE_NAME = evt.file_name;
            return transit< HandlingFileState >();
        }
};
class HandlingFileState:
    public fsm::state< HandlingFileState, PlayerFsm, PlayingState>
{
public:
    HandlingFileState( my_context ctx ): my_base( ctx){
        MCIWndOpen(_playerWnd, FILE_NAME.c_str(), NULL);
    }
};

class PlayingState:
    public fsm::simple_state< PlayingState, HandlingFileState>
{
public:
    PlayingState() {
        std::cout << "Entering PlayingState" << std::endl;
        std::cout << "fsm threadId = " << GetCurrentThreadId() << std::endl;
        assert(MCIWndPlay(_playerWnd) == 0);
        std::cout << "After MCIWndPlay" << std::endl;
    }
    typedef fsm::custom_reaction< UpdatePosEvent > reactions;
    fsm::result react( const UpdatePosEvent& evt){
        std::cout << "UpdatePosEvent is received pos = " << evt.pos << std::endl;
        return fsm::detail::result_utility::
            make_result( fsm::detail::consumed );
        }
private:
    long current_pos_;
};


PlayerFsm _player_fsm;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    AllocConsole();
    freopen("CONOUT$", "w+t", stdout);
    freopen("CONOUT$", "w+t", stderr);

    std::cout << "threadId = " << GetCurrentThreadId() << std::endl;
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_BUGFIX, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BUGFIX));

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra  = 0;
    wcex.cbWndExtra  = 0;
    wcex.hInstance   = hInstance;
    wcex.hIcon       = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_BUGFIX));
    wcex.hCursor     = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName  = MAKEINTRESOURCE(IDC_BUGFIX);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;

    hInst = hInstance; // Store instance handle in our global variable

    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
               CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    if (message == MCIWNDM_NOTIFYPOS){
        std::cout << "MCIWNDM_NOTIFYPOS received" << std::endl;
    }
    if (message == WM_COMMAND && LOWORD(wParam) == IDM_ABOUT){
        std::cout << "WM_COMMAND  received" << std::endl;
    }
    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            _player_fsm.process_event(OpenFileEvent(file_name));
            std::cout << "AFter process openfileevent" << std::endl;
            //DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_CREATE:
        _player_fsm.initiate();
        _playerWnd = MCIWndCreate(
            hWnd,
            hInst, 
            MCIWNDF_NOTIFYPOS|MCIWNDF_NOMENU|MCIWNDF_NOTIFYMODE,
            NULL);
        break;
    case MCIWNDM_NOTIFYPOS:
        //std::cout << "received MCIWNDM_NOTIFYPOS msg"
        //          << " threadId = " << GetCurrentThreadId()
        //          << " pos = " << lParam<< std::endl;
        //_player_fsm.process_event(UpdatePosEvent(lParam));
        break;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    std::cout << "Exit Winpro" << std::endl;
    return 0;
}
